#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include <omp.h>

#define BLOCK_LOW(id,p,n) (((id)*(n))/(p))
#define BLOCK_HIGH(id,p,n) (BLOCK_LOW((id)+1,(p),(n)) - 1)
#define STRSIZE 1000
#define NTHREAD_CRIVO 2

void clear_str(char *c, int n);
int palindromo(char *str);
int crivo(int np, int x);
void print_prime(int v[], int MAXNUM);
int next_prime(int v[], int ini, int MAXNUM);
void find_prime(int v[], int low, int high, int prime, int MAXNUM);


int main(int argc, char *argv[]) {

	FILE *fp;
	char aux[STRSIZE];
	int pa = 0,tpa=0;
	int primo=0, tprimo=0;
	int tamanho_frase=0;
	int tamanho_arquivo;
	int passo_arquivo;
	int inicio_arquivo;
	int i;
	int asc_palindromo;
	/*Variáveis MPI */   
	int dest,noProcesses, processId, src, tag;
	MPI_Status status;   

	/* Inicializacção do MPI */   
	MPI_Init(&argc, &argv);   
	MPI_Comm_size(MPI_COMM_WORLD, &noProcesses);
	MPI_Comm_rank(MPI_COMM_WORLD, &processId);

	if ( (fp = fopen(argv[1],"r")) == NULL ) 
	{
		printf("Erro ao abrir o arquivo.\n");
		exit(1);
	}


	clear_str(aux,STRSIZE);

	//divide os arquivos para cada processo
	fseek(fp, 0, SEEK_END);
	tamanho_arquivo= ftell(fp);
	passo_arquivo=(int)(tamanho_arquivo/noProcesses);
	inicio_arquivo=passo_arquivo*processId;
	//printf("processo %d inicio %d \n",processId,inicio_arquivo);	
	fseek(fp, inicio_arquivo, SEEK_SET);

	//le até a proxima palavra
	if(processId!=0)
		while (getc(fp)!= ' ');
	
			
	//agora vai até o final da frase
	tamanho_frase=1;
	primo=0;
	while(ftell(fp)< passo_arquivo*(processId + 1) && !feof(fp)) 
	{
		clear_str(aux,STRSIZE);
		fscanf(fp,"%s",aux);
		i=palindromo(aux);
		pa += i;
		if(i==1)//se por palindromo soma o ascc referente
		{
			asc_palindromo=0;
			for ( i = 0; i < strlen(aux); i++ ) 
			{
				if((int)aux[i]<65 || ((int)aux[i]>90 && (int)aux[i]<97) || (int)aux[i]>122 );
				else
					asc_palindromo+=(int)aux[i];
			}             
			
		        if (asc_palindromo > 0){ 
		        	primo += crivo(NTHREAD_CRIVO,asc_palindromo);
				//if(crivo(NTHREAD_CRIVO,asc_palindromo))
				//{
				//	printf("%s %d\n",aux,asc_palindromo);
				//}
			}

		}
	
   	}

	//recuperando os dados.
	tag=0;
	if(processId==0)//se for 0 recupera os resultados
	{
		tpa=pa;
		tprimo=primo;
		for (src=1; src < noProcesses; src++)
		{
			MPI_Recv(&pa, 1, MPI_DOUBLE, src, tag, MPI_COMM_WORLD,&status);
			tpa+=pa;
			MPI_Recv(&primo, 1, MPI_DOUBLE, src, tag, MPI_COMM_WORLD,&status);
			tprimo+=primo;
		}
		printf("Total de Palindromo : %d\n",tpa);
		printf("Total de Primo: %d\n",tprimo);
	}
	else //demais enviam resultado
	{
		dest = 0;       
		MPI_Send(&pa, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
		MPI_Send(&primo, 1, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD);
	}
	    

	MPI_Finalize( );

	fclose(fp);
	fflush(stdout);
	return 0;

}

void clear_str(char *c, int n) {

    int i = 0;

    for ( i = 0; i < n; i++ ) {
        c[i] = 0;
    }

}

int palindromo(char *str) {

    int i,l,j;
    int ssize = strlen(str);

    i=0;
    j=ssize-1;
    //ignorando caracteres especiais no final de palavra, no final da palavra, exem.: (kfbrwiufh)
    for(l=j;l>=0;l--)
        if((int)str[l]<65 || ((int)str[l]>90 && (int)str[l]<97) || (int)str[l]>122 )
                j--;
        else
                break;

    //ignorando caracteres especiais no inicio de palavra, no final da palavra, exem.: (kfbrwiufh)
    for(l=0;l<=j;l++)
        if((int)str[l]<65 || ((int)str[l]>90 && (int)str[l]<97) || (int)str[l]>122 )
                i++;
        else
                break;
    
    //se passar aq é palimdrome
    if(j==-1)
        return 0;//isso significa e existe uma palavra com apenas simbolos especiais
    for (  ;i < j;i++){
        if (   ((int)str[j] != (int)str[i])   &&         ((int)str[j]+32 != (int)str[i])   && ((int)str[j] != (int)str[i]+32)         )
                return 0;
        j--;
    }
    //printf("%s ",str);
    return 1;

}

int crivo(int np, int x) {

    int global_sum = 0;
    int i = 0;
    int MAXNUM = x+1;
    int v[MAXNUM];
    int low;
    int high;
    int p;

    for ( i = 0; i < MAXNUM; i++ )
        v[i] = 1;

    v[0] = 0;
    omp_set_num_threads(np);

    #pragma omp parallel shared(v) private(p,low,high)
    {
        p = 2;
        while (p*p <= MAXNUM) {

            low = BLOCK_LOW(omp_get_thread_num(),np,MAXNUM-p*p)+(p*p);
            high = BLOCK_HIGH(omp_get_thread_num(),np,MAXNUM-p*p)+(p*p);
            find_prime(v,low,high,p, MAXNUM);
            p = next_prime(v,p+1, MAXNUM);

        }
    }

    if (v[MAXNUM-1] == 1)
            return 1;
    else
            return 0;

}

void find_prime(int v[], int low, int high, int prime, int MAXNUM) {

    int j = 0;
    int k = 0;

    for ( j = low; (j%prime) != 0 && j < high && j < MAXNUM; j++ );
    if ( (j%prime) == 0 ) {
        v[j] = 0;

        for ( j; j <= high && j < MAXNUM; j += prime ) {
                v[j] = 0;
        }
    }

}

int next_prime(int v[], int ini, int MAXNUM) {

    int i = ini;
    while (v[i] == 0 && i < MAXNUM)
        i++;

    return i;

}

void print_prime(int v[], int MAXNUM) {

        int i = 0;
        for ( i = 0; i < MAXNUM; i++ )
            if ( v[i] ) printf("%d\n",i);

}

